package top.fullj.actor;

import top.fullj.actor.exception.ActorInitializationException;
import top.fullj.actor.exception.ActorKilledException;
import top.fullj.actor.exception.InvalidOperationException;

/**
 * base Actor
 *
 * @author wurenhai
 * @since 2018/3/11
 */
public abstract class Actor implements ActorContext.Receiver {

    /**
     * called before the Actor started (life cycle)
     */
    public void preStart() {}

    /**
     * called after the Actor stopped (life cycle)
     */
    public void postStop() {}

    /**
     * called before the Actor restarted
     */
    @SuppressWarnings("unused")
    public void preRestart(Exception reason, Object message) {
        stopChildren();
        postStop();
    }

    /**
     * called after the Actor restarted
     */
    @SuppressWarnings("unused")
    public void postRestart(Exception reason) {
        preStart();
    }

    void setContext(ActorContext ctx) {
        this.ctx = ctx;
    }

    /**
     * stop my child Actors
     */
    void stopChildren() {
        for (ActorRef child : ctx.children()) {
            ctx.stop(child);
        }
    }

    protected volatile ActorContext ctx;

    public final SupervisorStrategy strategy;

    Actor() {
        this(DEFAULT_STRATEGY);
    }

    Actor(SupervisorStrategy strategy) {
        this.strategy = strategy;
    }

    @SuppressWarnings("unused")
    protected ActorContext ctx() {
        return ctx;
    }

    protected ActorRef self() {
        return ctx.self;
    }

    protected boolean canReply(ActorRef sender) {
        return sender != ActorRef.NO_SENDER;
    }

    private static final SupervisorStrategy DEFAULT_STRATEGY = new OneForOneStrategy();

    @Override
    public void onReceive(ActorRef sender, Object message) {
        // default message handler
    }

    /**
     * supervisor strategy that decide how to handle exception
     */
    public static class SupervisorStrategy{

        /**
         * how to handle failure
         */
        public enum Directive {
            Escalate, Restart, Resume, Stop
        }

        public enum StrategyType {
            AllForOne, OneForOne
        }

        public final StrategyType type;

        public Directive decide(Exception e) {
            if (e instanceof ActorInitializationException) {
                return Directive.Stop;
            }
            if (e instanceof ActorKilledException) {
                return Directive.Stop;
            }
            if (e instanceof InvalidOperationException) {
                return Directive.Restart;
            }
            if (e instanceof RuntimeException) {
                return Directive.Restart;
            }
            return Directive.Escalate;
        }

        SupervisorStrategy(StrategyType type) {
            this.type = type;
        }
    }

    /**
     * apply to all siblings if a child failed
     */
    @SuppressWarnings("unused")
    public static final class AllForOneStrategy extends SupervisorStrategy {
        public AllForOneStrategy() {
            super(StrategyType.AllForOne);
        }
    }

    /**
     * apply only to the failed child
     */
    public static final class OneForOneStrategy extends SupervisorStrategy {
        OneForOneStrategy() {
            super(StrategyType.OneForOne);
        }
    }

}
